package com.lch.base.http;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.RawRes;
import android.text.TextUtils;

import com.lch.base.http.config.CacheProvide;
import com.lch.base.http.config.OkHttpProvide;
import com.lch.base.http.config.RetrofitProvide;
import com.lch.base.http.cookie.NovateCookieManager;
import com.lch.base.http.interfaces.HeadersInterceptor;
import com.lch.base.http.interfaces.ParamsInterceptor;
import com.lch.base.http.transformer.ITransformer;
import com.lch.base.http.transformer.TransformerGson;

import java.io.InputStream;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;

import okhttp3.Cache;
import okhttp3.CertificatePinner;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import retrofit2.CallAdapter;
import retrofit2.Converter;
import retrofit2.Retrofit;

/**
 * 网络请求基本参数封装类
 *
 * @author lee(29487113@qq.com) on 18/2/4
 */
public class HttpConfig {
    private static volatile HttpConfig mInstance;
    private Retrofit mRetrofit;
    private static volatile ApiService mService;
    private final ParamsInterceptor mParamsInterceptor;
    private final HeadersInterceptor mHeadersInterceptor;
    private static volatile ITransformer mTransformer; //数据解析器
    private static volatile HashMap<String, String> mErrorCodeMap; //跟服务器自定义的错误code // TODO: 2017/12/21 可以优化成注解
    // TODO: 2017/12/21 可以增加成功码的设置

    //构造函数私有，不允许外部调用

    private HttpConfig(Retrofit retrofit, Builder builder) {
        mRetrofit = retrofit;
        mService = mRetrofit.create(ApiService.class); //设置最基本的service

        mParamsInterceptor = builder.mParamsInterceptor;
        mHeadersInterceptor = builder.mHeadersInterceptor;
        mTransformer = builder.mTransformer;
        mErrorCodeMap = builder.mErrorCodeMap;
    }

    private static HttpConfig getInstance() {
        //build的时候已经初始化了，后面就不需要每次都判断是否初始化了，没有初始直接把错就行
        if (mInstance == null) {
            throw new NullPointerException("HttpConfig has not be initialized， you can init by HttpConfig.Builder.build()");
        }
        return mInstance;
    }

    public static ApiService getService() {
        return getInstance().mService;
    }

    public static ITransformer getTransformer() {
        return getInstance().mTransformer;
    }

    public static HashMap<String, String> getErrorCodeMap() {
        return getInstance().mErrorCodeMap;
    }

    /**
     * 获取参数拦截器
     * @return
     */
    public ParamsInterceptor getParamsInterceptor() {
        return mParamsInterceptor;
    }

    /**
     *
     * @return
     */
    public HeadersInterceptor getHeadersInterceptor() {
        return mHeadersInterceptor;
    }

    public static Map<String, String> interceptParams(Map<String, String> params) {
        if (params == null) {
            params = new HashMap<>();
        }
        if (mInstance.mParamsInterceptor != null) {
            params = mInstance.mParamsInterceptor.checkParams(params);
        }
        //retrofit的params的值不能为null，此处做下校验，防止出错
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (entry.getValue() == null) {
                params.put(entry.getKey(), "");
            }
        }
        return params;
    }

    public static Map<String, String> interceptHeaders(Map<String, String> headers) {
        if (headers == null) {
            headers = new HashMap<>();
        }
        if (mInstance.mHeadersInterceptor != null) {
            headers = mInstance.mHeadersInterceptor.checkHeaders(headers);
        }
        //retrofit的params的值不能为null，此处做下校验，防止出错
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            if (entry.getValue() == null) {
                headers.put(entry.getKey(), "");
            }
        }
        return headers;
    }


    public static class Builder {
        private Context mContext;
        private List<String> mServers = new ArrayList<>();

        private Retrofit.Builder mRetrofitBuilder;
        private Retrofit mRetrofit;

        private OkHttpClient.Builder mOkhttpBuilder;
        private OkHttpClient mOkHttpClient;

        private HashMap<String, String> mErrorCodeMap; //跟服务器自定义的错误code
        private ITransformer mTransformer; //数据解析处理器

        private ParamsInterceptor mParamsInterceptor;   //请求-参数拦截器，可以追加统一参数
        private HeadersInterceptor mHeadersInterceptor; //头部-参数拦截器，可以追加统一参数


        /**
         * @param context
         * @param baseUrl 如果外部不用baseUrl，调用时传完整的http://xxx.com/abc就行
         *                baseUrl 最后是否加/ 都可以。系统都会自动过滤
         */
        public Builder(Context context, @NonNull String baseUrl) {
            this(context, baseUrl, false);
        }

        public Builder(Context context, @NonNull String baseUrl, boolean isShowLog) {
            this(context, baseUrl, isShowLog, false, CacheProvide.CACHE_TYPE.CACHE_NONE);
        }

        /**
         * @param context
         * @param baseUrl base host
         * @param isCookie 是否使用默认cookie
         * @param cacheType 是否使用默认cache
         */
        public Builder(Context context, @NonNull String baseUrl, boolean isShowLog, boolean isCookie, CacheProvide.CACHE_TYPE cacheType) {
            if (TextUtils.isEmpty(baseUrl)) throw new NullPointerException("baseUrl == null");
            mContext = context;
            mRetrofitBuilder = RetrofitProvide.provideRetrofitBuild(baseUrl);
            mOkhttpBuilder = OkHttpProvide.provideOkHttpBuild(context, isShowLog, isCookie, cacheType);
            mTransformer = new TransformerGson();
        }

        /**
         * 设置OkHttpClient 可以整个由外部设置
         * @param client
         * @return
         */
        public Builder setClient(OkHttpClient client) {
            if (client == null) throw new NullPointerException("OkHttpClient is null");
            mOkHttpClient = client;
            return this;
        }

        /**
         * 备份服务器ip地址，可多次调用传递
         */
        public Builder setServerUrl(String server) {
            mServers.add(server);
            return this;
        }

        /**
         * 备份服务器ip地址
         */
        public Builder setServerUrls(List<String> servers) {
            mServers = servers;
            return this;
        }

        /**
         * 头部-参数拦截器，可以追加统一参数
         */
        public Builder setHeadersInterceptor(HeadersInterceptor interceptor) {
            mHeadersInterceptor = interceptor;
            return this;
        }

        /**
         * 请求-参数拦截器，可以追加统一参数
         */
        public Builder setParamsInterceptor(ParamsInterceptor interceptor) {
            mParamsInterceptor = interceptor;
            return this;
        }


        /**
         * 数据解析处理器
         */
        public Builder setTransformer(ITransformer transformer) {
            mTransformer = transformer;
            return this;
        }

        /**
         * 跟服务器定义的错误解析码
         */
        public Builder setErrorCodeMap(HashMap<String, String> errorCodeMap) {
            mErrorCodeMap = errorCodeMap;
            return this;
        }

        //okhttp设置==============================================
        /**
         * 设置超时时间 时间为秒
         * @param connectTimeout
         * @param readTimeout
         * @param writeTimeout
         * @return
         */
        public Builder setTimeOut(long connectTimeout, long readTimeout, long writeTimeout) {
            OkHttpProvide.setTimeOut(mOkhttpBuilder, connectTimeout, readTimeout, writeTimeout);
            return this;
        }

        /**
         * 设置超时时间，统一一个时间
         */
        public Builder setTimeOut(long timeout) {
            OkHttpProvide.setTimeOut(mOkhttpBuilder, timeout);
            return this;
        }


        public Builder connectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
            OkHttpProvide.connectionPool(mOkhttpBuilder, maxIdleConnections, keepAliveDuration, timeUnit);
            return this;
        }

        public Builder addInterceptor(Interceptor interceptor) {
            OkHttpProvide.addInterceptor(mOkhttpBuilder, interceptor);
            return this;
        }

        public Builder addNetworkInterceptor(Interceptor interceptor) {
            OkHttpProvide.addNetworkInterceptor(mOkhttpBuilder, interceptor);
            return this;
        }

        public Builder cookieManager(NovateCookieManager cookie) {
            OkHttpProvide.cookieJar(mOkhttpBuilder, cookie);
            return this;
        }

        //---------------https的认证------------------------------

        /**
         * 单向认证
         * @param certificates 服务器需要验证的证书 把证书放到raw目录下
         * @return
         */
        public Builder addSSLSocketFactory(@RawRes int[] certificates) {
            OkHttpProvide.addSSLSocketFactory(mOkhttpBuilder, mContext, certificates);
            return this;
        }

        /**
         * 双向认证
         * @param certificates 服务器需要验证的证书
         * @param bksFile 本地验证证书。一般双向验证才需要
         * @param password 本地验证证书的密码
         * @return
         */
        public Builder addSSLSocketFactory(InputStream[] certificates, InputStream bksFile, String password) {
            OkHttpProvide.addSSLSocketFactory(mOkhttpBuilder, certificates, bksFile, password);
            return this;
        }

        /**
         * 双向认证
         * @param certificates 服务器需要验证的证书 把证书放到raw目录下
         * @param clientKeyStoreBksFile 本地验证证书。一般双向验证才需要 把证书放到raw目录下
         * @param password 本地验证证书的密码
         * @return
         */
        public Builder addSSLSocketFactory(@RawRes int[] certificates, @RawRes int clientKeyStoreBksFile, String password) {
            OkHttpProvide.addSSLSocketFactory(mOkhttpBuilder, mContext, certificates, clientKeyStoreBksFile, password);
            return this;
        }


        public Builder addSSLSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
            OkHttpProvide.addSSLSocketFactory(mOkhttpBuilder, sslSocketFactory, trustManager);
            return this;
        }

        public Builder addHostnameVerifier(String[] hosts) {
            OkHttpProvide.addHostnameVerifier(mOkhttpBuilder, hosts);
            return this;
        }

        public Builder addHostnameVerifier(HostnameVerifier hostnameVerifier) {
            OkHttpProvide.addHostnameVerifier(mOkhttpBuilder, hostnameVerifier);
            return this;
        }
        //---------------https的认证------------------------------

        public Builder addCertificatePinner(CertificatePinner certificatePinner) {
            OkHttpProvide.addCertificatePinner(mOkhttpBuilder, certificatePinner);
            return this;
        }

        public Builder addCache(Cache cache) {
            OkHttpProvide.addCache(mOkhttpBuilder, cache);
            return this;
        }

        public Builder proxy(Proxy proxy) {
            OkHttpProvide.proxy(mOkhttpBuilder, proxy);
            return this;
        }

        //okhttp设置==============================================


        //Retrofit设置==============================================

        /**
         * 解析器
         */
        public Builder setConverterFactory(Converter.Factory factory) {
            mRetrofitBuilder.addConverterFactory(factory);
            return this;
        }

        /**
         * 解析器 不传默认RxJavaCallAdapterFactory
         */
        public Builder setCallAdapterFactory(CallAdapter.Factory factory) {
            mRetrofitBuilder.addCallAdapterFactory(factory);
            return this;
        }

        // TODO: 2017/12/22 作用
        public Builder callFactory(okhttp3.Call.Factory factory) {
            mRetrofitBuilder.callFactory(factory);
            return this;
        }

        //Retrofit设置==============================================

        public HttpConfig build() {
            //判断外部有没有设置mOkHttpClient，没有就用默认的mOkhttpBuilder build一个出来
            if (mOkHttpClient == null) {
                mOkHttpClient = mOkhttpBuilder.build();
            }
            mRetrofitBuilder.client(mOkHttpClient);
            mRetrofit = mRetrofitBuilder.build();
            mInstance = new HttpConfig(mRetrofit, this);  //这里初始化的时候设置全局，后面就不需要每次都判断是否初始化了
            return mInstance;
        }
    }
}
